/*
 *  Copyright 2007 Genoworks
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License. 
 */

package com.genoworks.retriever.bigdir;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.List;
import java.util.TreeSet;

/**
 * A real file on disk that can be grow
 * up to 2GB(or more). It accesses the
 * real file with
 * {@link RandomAccessFile}.
 * 
 * @author appler
 * 
 */
class BigDiskFile implements Comparable<BigDiskFile> {

  private int id = 0;

  private final long maxSize;

  private long curSize = 0L;

  private File file = null;

  private RandomAccessFile accessor = null;

  /**
   * List of segments stored on this
   * data file.
   */
  private TreeSet<BigFileSegment> segments = new TreeSet<BigFileSegment>();

  int getId() {
    return id;
  }

  BigDiskFile( File f, int i, long max ) throws FileNotFoundException {
    id = i;
    maxSize = max;
    file = f;
    accessor = new RandomAccessFile( file, "rw" ); //$NON-NLS-1$
  }

  long getTotalSpace() throws IOException {
    return accessor.length();
  }

  void setVirtualFiles( List<BigFile> files ) {
    for ( BigFile afile : files ) {
      for ( BigFileSegment seg : afile.getSegments() ) {
        if ( seg.getSource() == id ) {
          segments.add( seg );
          curSize += seg.getLength();
        }
      }
    }
  }

  long getEmptySpace() {
    return maxSize - curSize;
  }

  void close() throws IOException {
    accessor.close();
  }

  BigFileSegment createNextSegment( int length ) {

    //
    long oldPos = 0;

    //
    for ( BigFileSegment seg : segments ) {
      long curPos = seg.getOffset();
      int len = (int) ( curPos - oldPos );
      if ( len > 0 ) {
        int min = Math.min( length, len );
        BigFileSegment result = new BigFileSegment( id, oldPos, min );
        segments.add( result );
        curSize += min;
        return result;
      }
      oldPos = curPos + seg.getLength();
    }

    //
    long empty = getEmptySpace();
    if ( empty > 0 ) {
      int min = (int) Math.min( empty, length );
      BigFileSegment result = new BigFileSegment( id, oldPos, min );
      segments.add( result );
      curSize += min;
      return result;
    }

    /*
     * couldn't make a segment on this
     * data file.
     */
    return null;
  }

  public int compareTo( BigDiskFile f ) {
    if ( id < f.id ) {
      return -1;
    } else if ( id > f.id ) {
      return 1;
    }
    return 0;
  }

  void write( long position, byte[] buffer, int offset, int length )
      throws IOException {

    //
    accessor.seek( position );
    accessor.write( buffer, offset, length );
  }

  void read( BigFileSegment seg, long pos, byte[] buffer, int offset, int length )
      throws IOException {

    //
    accessor.seek( seg.getOffset() + pos );
    accessor.read( buffer, offset, length );
  }

  void deleteSegment( BigFileSegment seg ) {
    segments.remove( seg );
    curSize -= seg.getLength();
  }

  void updateSegment( BigFileSegment seg ) {
    segments.add( seg );
    curSize += seg.getLength();
  }

  public String toString() {
    return "BigDiskFile{id=" + id + ",max=" + maxSize + ",cur=" + curSize + "}";
  }
}
